package com.atguigu.gmall.search.service.impl;
import com.atguigu.gmall.search.vo.SearchRespVo.OrderMap;

import com.atguigu.gmall.search.entity.Goods;
import com.atguigu.gmall.search.repo.GoodsRepository;
import com.atguigu.gmall.search.service.SearchService;
import com.atguigu.gmall.search.vo.SearchParamVo;
import com.atguigu.gmall.search.vo.SearchRespVo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author tangsy
 * @Description TODO
 * @date 2022/12/12 18:55
 * @Version 1.0
 */
@Service
public class SearchServiceImpl implements SearchService {

    int pageSize = 10;

    @Autowired
    GoodsRepository goodsRepository;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public SearchRespVo search(SearchParamVo vo) {

        //1 查询条件
        Query query = buildQuery(vo);

        SearchHits<Goods> result = elasticsearchRestTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));

        SearchRespVo respVo = buildSearchResp(result,vo);

        return respVo;
    }

    private SearchRespVo buildSearchResp(SearchHits<Goods> result,SearchParamVo vo) {
        SearchRespVo searchRespVo = new SearchRespVo();
        //1 检索参数
        searchRespVo.setSearchParam(vo);

        //2 品牌面包屑
        if (!StringUtils.isEmpty(vo.getTrademark())){
            searchRespVo.setTrademarkParam("品牌:" + vo.getTrademark().split(":")[1]);
        }

        //3 属性面包屑
        if (vo.getProps()!=null && vo.getProps().length>0){
            List<SearchRespVo.Props> collect = Arrays.stream(vo.getProps())
                    .map(item -> {
                        String[] spilt = item.split(":");
                        SearchRespVo.Props props = new SearchRespVo.Props();
                        props.setAttrName(spilt[2]);
                        props.setAttrValue(spilt[1]);
                        props.setAttrId(Long.parseLong(spilt[0]));
                        return props;
                    }).collect(Collectors.toList());
            searchRespVo.setPropsParamList(collect);
        }

        //4 品牌列表 聚合 aggregations：
        ParsedLongTerms tmIdAgg = result.getAggregations().get("tmIdAgg");

        List<SearchRespVo.Trademark> trademarks = tmIdAgg.getBuckets().stream().map(bucket -> {
                    SearchRespVo.Trademark trademark = new SearchRespVo.Trademark();
                    long tmId = bucket.getKeyAsNumber().longValue();
                    trademark.setTmId(tmId);

                    ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
                    String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
                    trademark.setTmName(tmName);

                    ParsedStringTerms tmLogoAgg = bucket.getAggregations().get("tmLogoAgg");
                    String tmLogo = tmLogoAgg.getBuckets().get(0).getKeyAsString();
                    trademark.setTmLogoUrl(tmLogo);
                    return trademark;
                }).collect(Collectors.toList());

        searchRespVo.setTrademarkList(trademarks);

        //5 属性列表
        ParsedNested attrAgg = result.getAggregations().get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");

        List<SearchRespVo.Attrs> attrsList = attrIdAgg.getBuckets().stream().map(bucket -> {
                    SearchRespVo.Attrs attrs = new SearchRespVo.Attrs();
                    //属性id
                    long attrId = bucket.getKeyAsNumber().longValue();
                    attrs.setAttrId(attrId);
                    //属性名
                    ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
                    String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
                    attrs.setAttrName(attrName);
                    //属性值
                    ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
                    List<String> valueList = attrValueAgg.getBuckets().stream()
                            .map(item -> item.getKeyAsString())
                            .collect(Collectors.toList());
                    attrs.setAttrValueList(valueList);
                    return attrs;
                }).collect(Collectors.toList());

        searchRespVo.setAttrsList(attrsList);

        //6 url参数
        String urlParam = buildUrlParam(vo);
        searchRespVo.setUrlParam(urlParam);

        //7 排序信息
        if(!StringUtils.isEmpty(vo.getOrder())){
            OrderMap orderMap = new OrderMap();
            String[] split = vo.getOrder().split(":");
            orderMap.setType(split[0]);
            orderMap.setSort(split[1]);
            searchRespVo.setOrderMap(orderMap);
        }

        //8 商品列表
        List<Goods> goods = result.getSearchHits().stream().map(item ->{
            Goods content = item.getContent();
            if (!StringUtils.isEmpty(vo.getKeyword())){
                String title = item.getHighlightField("title").get(0);
                content.setTitle(title);
            }
            return content;
        }).collect(Collectors.toList());
        searchRespVo.setGoodsList(goods);

        //9 页码
        searchRespVo.setPageNo(vo.getPageNo());
        //总记录
        //总页码： 总记录数%每页大小==0？总记录数/每页大小:总记录数/每页大小 + 1
        long totalHits = result.getTotalHits();
        searchRespVo.setTotalPages(totalHits % pageSize == 0 ? totalHits / pageSize : totalHits / pageSize + 1);

        return searchRespVo;
    }

    private String buildUrlParam(SearchParamVo vo) {

        StringBuilder builder = new StringBuilder("list.html?");

        //分类信息
        if (!StringUtils.isEmpty(vo.getCategory1Id())){
            builder.append("&category1Id=" + vo.getCategory1Id());
        }
        if (!StringUtils.isEmpty(vo.getCategory2Id())){
            builder.append("&category2Id=" + vo.getCategory2Id());
        }
        if (!StringUtils.isEmpty(vo.getCategory3Id())){
            builder.append("&category3Id=" + vo.getCategory3Id());
        }

        //keyword
        if (!StringUtils.isEmpty(vo.getKeyword())){
            builder.append("&keyword=" + vo.getKeyword());
        }

        //品牌
        if (!StringUtils.isEmpty(vo.getTrademark())){
            builder.append("&trademark=" + vo.getTrademark());
        }

        //属性
        if (vo.getProps()!=null && vo.getProps().length>0){
            Arrays.stream(vo.getProps()).forEach(item -> {
                builder.append("&props=" + item);
            });
        }

        return builder.toString();
    }

    private Query buildQuery(SearchParamVo vo){
        //1 查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //1.1 一级分类
        if (vo.getCategory1Id() != null) {
            TermQueryBuilder term = QueryBuilders.termQuery("category1Id", vo.getCategory1Id());
            boolQuery.must(term);
        }
        //1.2 二级分类
        if (vo.getCategory2Id() != null) {
            TermQueryBuilder term = QueryBuilders.termQuery("category2Id", vo.getCategory2Id());
            boolQuery.must(term);
        }
        //1.3 三级分类
        if (vo.getCategory3Id() != null) {
            TermQueryBuilder term = QueryBuilders.termQuery("category3Id", vo.getCategory3Id());
            boolQuery.must(term);
        }
        //1.4 关键字
        if (vo.getKeyword() != null) {
            MatchQueryBuilder match = QueryBuilders.matchQuery("title", vo.getKeyword());
            boolQuery.must(match);
        }
        //1.5 品牌
        if (!StringUtils.isEmpty(vo.getTrademark())){
            String[] split = vo.getTrademark().split(":");
            TermQueryBuilder term = QueryBuilders.termQuery("tmId", split[0]);
            boolQuery.must(term);
        }
        //1.6 属性查询
        if (vo.getProps() != null && vo.getProps().length > 0){
            Arrays.stream(vo.getProps()).forEach(item -> {
                String[] split = item.split(":");
                BoolQueryBuilder bool = QueryBuilders.boolQuery();
                bool.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                bool.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
                NestedQueryBuilder nested = QueryBuilders.nestedQuery("attrs",bool, ScoreMode.None);
                boolQuery.must(nested);
            });
        }

        NativeSearchQuery query = new NativeSearchQuery(boolQuery);

        //2 排序
        if (!StringUtils.isEmpty(vo.getOrder())){
            String[] split = vo.getOrder().split(":");
            Sort.Direction direction = "asc".equals(split[1])?Sort.Direction.ASC:Sort.Direction.DESC;
            Sort sort = null;
            switch (split[0]){
                case "1":
                    sort = Sort.by(direction, "hotScore");
                    break;
                case "2":
                    sort = Sort.by(direction, "price");
                    break;
                default:
                    sort = Sort.by(Sort.Direction.DESC, "hotScore");
            }
            query.addSort(sort);
        }

        //3 分页
        Pageable pageable = PageRequest.of(vo.getPageNo() - 1,10);
        query.setPageable(pageable);

        //4 高亮
        if (!StringUtils.isEmpty(vo.getKeyword())){
            HighlightBuilder builder = new HighlightBuilder()
                    .field("title")
                    .preTags("<span style='color:red'>")
                    .postTags("</span>");

            HighlightQuery highlightQuery = new HighlightQuery(builder);
            query.setHighlightQuery(highlightQuery);
        }

        //5 聚合
        // 品牌聚合
        TermsAggregationBuilder tmIdAgg = AggregationBuilders.terms("tmIdAgg")
                .field("tmId")
                .size(200);

        TermsAggregationBuilder tmNameAgg = AggregationBuilders.terms("tmNameAgg")
                .field("tmName")
                .size(1);
        tmIdAgg.subAggregation(tmNameAgg);
        TermsAggregationBuilder tmLogoAgg = AggregationBuilders.terms("tmLogoAgg")
                .field("tmLogoUrl")
                .size(1);
        tmIdAgg.subAggregation(tmLogoAgg);
        query.addAggregation(tmIdAgg);

        // 属性聚合
        NestedAggregationBuilder attrAgg = AggregationBuilders.nested("attrAgg", "attrs");

        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg")
                .field("attrs.attrId")
                .size(200);
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg")
                .field("attrs.attrName")
                .size(1);
        attrIdAgg.subAggregation(attrNameAgg);
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg")
                .field("attrs.attrValue")
                .size(200);
        attrIdAgg.subAggregation(attrValueAgg);
        attrAgg.subAggregation(attrIdAgg);
        query.addAggregation(attrAgg);

        return query;
    }

    @Override
    public void up(Goods goods) {
        goodsRepository.save(goods);
    }

    @Override
    public void down(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    @Override
    public void updateHotScore(Long skuId, Long score) {
        Document document = Document.create();
        document.put("hotScore",score);           
        //增量更新
        UpdateQuery updateQuery = UpdateQuery.builder("" + skuId)
                .withDocAsUpsert(true)
                .withDocument(document)
                .build();

        elasticsearchRestTemplate.update(updateQuery,IndexCoordinates.of("goods"));
    }
}
